﻿/// Copyright (c) 2012 John Tear
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
/// and associated documentation files (the "Software"), to deal in the Software without restriction, 
/// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
/// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
/// subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all copies or substantial 
/// portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
/// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 
/// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
/// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
/// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
///


using System;
using System.Collections.Generic;
using System.Text;
using System.Net;

namespace com.feelgoodsoftware.sharptftp
{
    /// <summary>
    /// A class to manage a tftp client request (used in tftp server).
    /// </summary>
    public class TftpClientRequest
    {

        /// <summary>
        /// Is the request a read (RRQ) or write (WRQ) request.
        /// </summary>
        public TftpOpCode RequestType { get; set; }

        /// <summary>
        /// The filename in the original RRQ/WRQ
        /// </summary>
        public string Filename { get; set; }

        /// <summary>
        /// used to manage connection timeouts, each time a packet is received from a
        /// client this should be reset
        /// </summary>
        public DateTime LastPacketReceived { get; set; }

        /// <summary>
        /// used to manage acknowledgements.
        /// </summary>
        public short LastBlockNumberReceived { get; set; }

        /// <summary>
        /// The data buffer for sending and recieving. Each time a packet arrives or is sent
        /// the remaining/received data field is buffered in here, when the request is completed
        /// it will be empty (RRQ) or it will be full (WRQ) waiting disk storage.
        /// </summary>
        public byte[] DataBuffer { get; set; }

        /// <summary>
        /// Stores the last data block sent, this can be helpful if a tftp client does not acknowledge
        /// the block and it needs to be sent again.
        /// </summary>
        public byte[] LastDataBlockSent { get; set; }

        /// <summary>
        /// The client endpoint making the request.
        /// </summary>
        public IPEndPoint Client { get; set; }

        /// <summary>
        /// The block size in each data field, default is 512.
        /// </summary>
        public int NegotiatedBlockSize { get; set; }

        /// <summary>
        /// Indicates if the request is completed.
        /// </summary>
        public bool RequestComplete { get; set; }

        /// <summary>
        /// Constructor.
        /// </summary>
        public TftpClientRequest()
        {
            LastPacketReceived = DateTime.Now;
            DataBuffer = new byte[0];
            LastBlockNumberReceived = -1;
            NegotiatedBlockSize = 512;
        }

        /// <summary>
        /// Outpust a socket value of the end point (as a reference for Dictionary entry).
        /// </summary>
        /// <returns>A string containing the socket of the endpoint, i.e. 192.168.1.1:1234</returns>
        public override string ToString()
        {
            if (Client != null)
            {
                return Client.Address.ToString() + ":" + Client.Port;
            }
            else
            {
                return "Unknown";
            }
        }

        /// <summary>
        /// Gets the next data block to send to a tftp client making a RRQ. The block is also removed from the data buffer
        /// and placed in the LastDataBlockSent property.
        /// </summary>
        /// <returns>The data block to send.</returns>
        public byte[] GetNextDataBlockToSend()
        {
            // return data to send back
            byte[] returnData = new byte[1] { (byte)0 };

            // get the next block
            if (DataBuffer.Length > NegotiatedBlockSize)
            {
                returnData = new byte[NegotiatedBlockSize];

                // copy the block
                for (int i = 0; i < NegotiatedBlockSize; i++)
                {
                    returnData[i] = DataBuffer[i];
                }

                byte[] newBuffer = new byte[DataBuffer.Length - NegotiatedBlockSize];

                // trim the DataBuffer
                for (int i = NegotiatedBlockSize; i < DataBuffer.Length; i++)
                {
                    newBuffer[i - NegotiatedBlockSize] = DataBuffer[i];
                }

                DataBuffer = newBuffer;
            }
            else
            {
                if (DataBuffer.Length != 0)
                {
                    // send the remaining data
                    returnData = DataBuffer;
                    DataBuffer = new byte[0];
                }
            }

#if DEBUG
            Console.WriteLine("returnData length={0}", returnData.Length);
            Console.WriteLine("DataBuffer length={0}", DataBuffer.Length);
#endif

            return returnData;
        }
    }
}
